﻿using System;
using System.Net;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;


//Server port should be from 49152 through 65535.
//http://www.iana.org/assignments/port-numbers

namespace Voize
{
    internal class CServer
    {



        internal CServer(string ip, int port,CRemotePlayer RemotePlayer)
        {
            m_Ip = ip;
            m_Port = port;
            m_RemotePlayer = RemotePlayer;
        }



        internal CServer(int port, CRemotePlayer RemotePlayer)
        {
            m_Port = port;
            m_RemotePlayer = RemotePlayer;
        }



        private int m_Port;
        private string m_Ip = "";
        private TcpListener m_Server;
        private CRemotePlayer m_RemotePlayer;
        private bool m_IsRunning = false;
        TcpClient m_Accepted_client;
        private string m_CurrentPassword = "";
        private string m_CurrentUser = "";



        internal void Stop()
        {
            if (m_Server != null)
            {
                m_IsRunning = false;
                if(m_Accepted_client != null)
                    m_Accepted_client.Close();
                m_Server.Stop();
            }
        }



        internal void Start()
        {
            Thread command_thread;

            if (m_Server == null)
                if (m_Ip == "")
                    m_Server = this.CreateClient("", m_Port);
                else
                    m_Server = this.CreateClient(m_Ip, m_Port);

            m_IsRunning = true;

            //start new thread for listening clients
            command_thread = new Thread(new ThreadStart(WaitForClients));
            command_thread.Start();
        }



        private TcpListener CreateClient(string ip, int port)
        {
            TcpListener srv;

            if (ip == "")
                srv = new TcpListener(IPAddress.Any, port);
            else
            {
                srv = new TcpListener(IPAddress.Parse(ip), port);
            }

            return srv;
        }



        private void WaitForClients()
        {
            if (m_Server != null)
            {
                try
                {
                    m_Server.Start();
                }
                catch (SocketException e)
                {
                    MessageBox.Show("A Socket exception occurred when starting the server: " + e.Message + "." +
                        " Error code: " + e.ErrorCode.ToString(), "Socket exception. Unable to start the server.", 
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                }

                if (m_IsRunning)
                {
                    while (true)
                    {
                        try
                        {
                            m_Accepted_client = m_Server.AcceptTcpClient();

                            //start new thread for parsing client requests. AcceptTcpClient() is a blocking call
                            Thread client_thread = new Thread(new ParameterizedThreadStart(ReadClient));
                            client_thread.Start(m_Accepted_client);
                        }
                        catch (SocketException e)
                        { //Workaround for the WSACancelBlockingCall exception when the server is stopped.
                            if(e.ErrorCode == 10004)
                                break; 
                            else
                                MessageBox.Show("A Socket exception occurred int WaitForClients(): " + e.Message + "." +
                                        " Error code: " + e.ErrorCode.ToString(), "Socket exception", MessageBoxButtons.OK,
                                        MessageBoxIcon.Error);
                            m_IsRunning = false;
                            this.Stop();
                            break;
                        } 
                    }
                }
            }
        }



        private void ReadClient(object accepted_client)
        {
            if (accepted_client.GetType() == typeof(TcpClient))
            {
                TcpClient client = (TcpClient)accepted_client;
                NetworkStream coming_stream = client.GetStream();
                byte[] command_buffer = new byte[4096];
                int byte_count;
                ASCIIEncoding asc_enc = new ASCIIEncoding();

                while (true)
                {
                    byte_count = 0;

                    try
                    {
                        byte_count = coming_stream.Read(command_buffer, 0, 4096);
                    }
                    catch { break; }//Socket error, should this just fail silently?
                       
                    if (byte_count == 0)
                        break; //Client disconnected?

                    //Received request from client ok.
                    this.ParseCommand(asc_enc.GetString(command_buffer,0,byte_count),coming_stream);
                }

                client.Close();
            }
        }


        private void ParseCommand(string command_message,NetworkStream coming_stream)
        {
            byte[] response_buffer;
            ASCIIEncoding asc_enc = new ASCIIEncoding();

            if (command_message.StartsWith("auth") || command_message.StartsWith("play"))
            {
                if (command_message.StartsWith("auth"))
                {
                    string[] UserArray = command_message.Split(';');
                    this.AUTH(coming_stream, UserArray[2], UserArray[1]);
                }
                else
                {
                    CPlayerStatus.PlayListLoaded = false;
                    string[] tracknumber = command_message.Split(';');
                    this.PLAY(coming_stream,tracknumber[1]);
                }
            }
            else
            {

                switch (command_message)
                {
                    case "list":
                        this.LIST(coming_stream);
                        break;
                    case "stop":
                        this.STOP(coming_stream);
                        break;
                    case "pause":
                        this.PAUSE(coming_stream);
                        break;
                    case "next":
                        this.NEXT(coming_stream);
                        break;
                    case "previous":
                    case "prev":
                        this.PREVIOUS(coming_stream);
                        break;
                    case "help":
                        this.HELP(coming_stream);
                        break;
                    case "auth":
                        this.AUTH(coming_stream, m_CurrentUser, m_CurrentPassword);
                        break;
                    default:
                        response_buffer = asc_enc.GetBytes("Command not recognized.");
                        coming_stream.Write(response_buffer, 0, response_buffer.Length);
                        coming_stream.Flush();
                        this.HELP(coming_stream);
                        break;
                }
            }
        }



        #region Commands
        private void HELP(NetworkStream coming_stream)
        {
            ASCIIEncoding asc_enc = new ASCIIEncoding();
            byte[] response_buffer;

            response_buffer = asc_enc.GetBytes("Available commands: list play stop pause next prev help");
            coming_stream.Write(response_buffer, 0, response_buffer.Length);
            coming_stream.Flush();
        }



        //List the current playlist
        private void LIST(NetworkStream coming_stream)
        {
            ASCIIEncoding asc_enc = new ASCIIEncoding();
            byte[] response_buffer;

            for(int i = 0; i< m_RemotePlayer.Player.Playlist.SoundCollection.Count -1;i++)
            {               
                response_buffer = asc_enc.GetBytes(i.ToString() + ":" + 
                                               m_RemotePlayer.Player.Playlist.Songs[0,i].Value + "\n");

                coming_stream.Write(response_buffer, 0, response_buffer.Length);
                coming_stream.Flush();
                response_buffer = null;
            }
        }



        private void PLAY(NetworkStream coming_stream,string tracknumber)
        {           
            m_RemotePlayer.Player.Playlist.SetSelectedSong(Int32.Parse(tracknumber));

            //m_RemotePlayer.Player.Play(
            //   m_RemotePlayer.Player.Playlist.GetSong(Int32.Parse(tracknumber)),
            //   1, 0, 1, m_RemotePlayer.Player.Volume);

            SendMessage(coming_stream, "Playing: " +
                   m_RemotePlayer.Player.Playlist.Songs[0, m_RemotePlayer.Player.Playlist.CurrentSongIndex].Value);
        }



        private void STOP(NetworkStream coming_stream)
        {
            m_RemotePlayer.Player.Stop();
            SendMessage(coming_stream, "Player stopped.");
        }



        private void PAUSE(NetworkStream coming_stream)
        {
            if (m_RemotePlayer.Player.IsPaused)
            {
                m_RemotePlayer.Player.Pause(false);
                SendMessage(coming_stream, "Playing: " +
                    m_RemotePlayer.Player.Playlist.Songs[0,m_RemotePlayer.Player.Playlist.CurrentSongIndex + 1].Value);
            }
            else
            {
                m_RemotePlayer.Player.Pause(true);
                SendMessage(coming_stream, "Player paused.");
            }

        }



        private void NEXT(NetworkStream coming_stream)
        {
            CPlayerStatus.PlayListLoaded = false;
            m_RemotePlayer.Player.Playlist.NextSong();
            SendMessage(coming_stream, "Playing: " +
               m_RemotePlayer.Player.Playlist.Songs[0, m_RemotePlayer.Player.Playlist.CurrentSongIndex + 1].Value);
        }



        private void PREVIOUS(NetworkStream coming_stream)
        {
            CPlayerStatus.PlayListLoaded = false;
            m_RemotePlayer.Player.Playlist.PreviousSong();
            SendMessage(coming_stream, "Playing: " +
               m_RemotePlayer.Player.Playlist.Songs[0, m_RemotePlayer.Player.Playlist.CurrentSongIndex - 1].Value);
        }
        #endregion



        internal bool IsRunning
        {
            get { return m_IsRunning; }
        }



        private void SendMessage(NetworkStream coming_stream,string message)
        {
            ASCIIEncoding asc_enc = new ASCIIEncoding();
            byte[] response_buffer;

            response_buffer = asc_enc.GetBytes(message);
            coming_stream.Write(response_buffer, 0, response_buffer.Length);
            coming_stream.Flush();

        }



        private void AUTH(NetworkStream coming_stream,string passwd,string user)
        {
            if (passwd == "" || user == "")
                SendMessage(coming_stream, "fail");
            else
            {

                bool ok = false;
                CRemoteAuthentication autentication = new CRemoteAuthentication();

                ok = autentication.Authenticate(user, passwd);

                if (ok == true)
                    SendMessage(coming_stream, "ok");
                else
                    SendMessage(coming_stream, "fail");
            }
        }
    }
}
